package com.seecen.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seecen.pojo.dto.TallyDto;
import com.seecen.pojo.entity.Tally;
import com.seecen.pojo.vo.BillDetailVO;
import com.seecen.pojo.vo.BillItemVO;
import com.seecen.pojo.vo.ChartDataVO;
import com.seecen.pojo.vo.RankingItemVO;
import com.seecen.service.TallyService;
import com.seecen.mapper.TallyMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

/**
* @author xuxin
* @description 针对表【t_tally(用户记账记录表)】的数据库操作Service实现
* @createDate 2025-10-16 10:19:26
*/
@Slf4j
@Service
public class TallyServiceImpl extends ServiceImpl<TallyMapper, Tally>
    implements TallyService{

    // 分类映射 (支出类型)
    private static final Map<Long, String> EXPENSE_CATEGORY_MAP = new HashMap<>();
    // 分类映射 (收入类型)
    private static final Map<Long, String> INCOME_CATEGORY_MAP = new HashMap<>();
    
    static {
        // 初始化支出分类映射
        EXPENSE_CATEGORY_MAP.put(1L, "餐饮");
        EXPENSE_CATEGORY_MAP.put(2L, "购物");
        EXPENSE_CATEGORY_MAP.put(3L, "日用");
        EXPENSE_CATEGORY_MAP.put(4L, "交通");
        EXPENSE_CATEGORY_MAP.put(5L, "蔬菜");
        EXPENSE_CATEGORY_MAP.put(6L, "水果");
        EXPENSE_CATEGORY_MAP.put(7L, "设置");
        
        // 初始化收入分类映射
        INCOME_CATEGORY_MAP.put(1L, "工资");
        INCOME_CATEGORY_MAP.put(2L, "兼职");
        INCOME_CATEGORY_MAP.put(3L, "理财");
        INCOME_CATEGORY_MAP.put(4L, "礼金");
        INCOME_CATEGORY_MAP.put(5L, "其他");
        INCOME_CATEGORY_MAP.put(7L, "设置");
    }

    @Override
    public ChartDataVO getChartData(String type, String period, Long userId) {
        log.info("获取图表数据 - userId: {}, type: {}, period: {}", userId, type, period);
        
        // 获取日期范围
        Date[] dateRange = getDateRange(period);
        Date startDate = dateRange[0];
        Date endDate = dateRange[1];
        
        log.info("日期范围 - startDate: {}, endDate: {}", startDate, endDate);
        
        // 查询数据
        LambdaQueryWrapper<Tally> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Tally::getUserId, userId)
               .eq(Tally::getType, "expense".equals(type) ? 1L : 2L)
               .between(Tally::getCreateTime, startDate, endDate)
               .eq(Tally::getIsDeleted, 0);
        
        List<Tally> tallyList = this.list(wrapper);
        log.info("查询到 {} 条记账记录", tallyList.size());
        
        // 生成日期列表
        List<String> dates = generateDateList(period, startDate, endDate);
        log.info("生成日期列表: {}", dates);
        
        // 按日期分组统计
        Map<String, Double> dateAmountMap = new HashMap<>();
        SimpleDateFormat dateFormat = getDateFormat(period);
        
        for (Tally tally : tallyList) {
            String dateKey = dateFormat.format(tally.getCreateTime());
            dateAmountMap.put(dateKey, dateAmountMap.getOrDefault(dateKey, 0.0) + tally.getPrice());
        }
        
        log.info("日期金额映射: {}", dateAmountMap);
        
        // 构建values数组
        List<Double> values = new ArrayList<>();
        for (String date : dates) {
            values.add(dateAmountMap.getOrDefault(date, 0.0));
        }
        
        // 计算统计数据
        double total = values.stream().mapToDouble(Double::doubleValue).sum();
        double average = values.isEmpty() ? 0 : total / values.size();
        double max = values.stream().mapToDouble(Double::doubleValue).max().orElse(0.0);
        
        ChartDataVO result = new ChartDataVO(dates, values, total, average, max);
        log.info("返回图表数据 - total: {}, average: {}, max: {}", total, average, max);
        
        return result;
    }

    @Override
    public List<RankingItemVO> getRankingData(String type, String period, Long userId) {
        log.info("获取排行榜数据 - userId: {}, type: {}, period: {}", userId, type, period);
        
        // 获取日期范围
        Date[] dateRange = getDateRange(period);
        Date startDate = dateRange[0];
        Date endDate = dateRange[1];
        
        // 查询数据
        LambdaQueryWrapper<Tally> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Tally::getUserId, userId)
               .eq(Tally::getType, "expense".equals(type) ? 1L : 2L)
               .between(Tally::getCreateTime, startDate, endDate)
               .eq(Tally::getIsDeleted, 0);
        
        List<Tally> tallyList = this.list(wrapper);
        log.info("查询到 {} 条记账记录", tallyList.size());
        
        // 按分类统计
        Map<Long, Double> categoryAmountMap = new HashMap<>();
        for (Tally tally : tallyList) {
            Long flag = tally.getFlag();
            categoryAmountMap.put(flag, categoryAmountMap.getOrDefault(flag, 0.0) + tally.getPrice());
        }
        
        log.info("分类金额映射: {}", categoryAmountMap);
        
        // 计算总金额
        double total = categoryAmountMap.values().stream().mapToDouble(Double::doubleValue).sum();
        
        // 构建排行榜列表
        Map<Long, String> categoryMap = "expense".equals(type) ? EXPENSE_CATEGORY_MAP : INCOME_CATEGORY_MAP;
        List<RankingItemVO> rankingList = new ArrayList<>();
        
        for (Map.Entry<Long, Double> entry : categoryAmountMap.entrySet()) {
            String categoryName = categoryMap.getOrDefault(entry.getKey(), "其他");
            Double amount = entry.getValue();
            Double percentage = total > 0 ? (amount / total) * 100 : 0;
            
            rankingList.add(new RankingItemVO(categoryName, amount, percentage));
        }
        
        // 按金额降序排序
        rankingList.sort((a, b) -> Double.compare(b.getAmount(), a.getAmount()));
        
        log.info("返回排行榜数据 - 共 {} 个分类, 总金额: {}", rankingList.size(), total);
        
        return rankingList;
    }

    /**
     * 获取日期范围
     */
    private Date[] getDateRange(String period) {
        LocalDate now = LocalDate.now();
        LocalDate startDate;
        LocalDate endDate = now;
        
        switch (period) {
            case "week":
                // 本周：从周一到今天
                startDate = now.minusDays(now.getDayOfWeek().getValue() - 1);
                break;
            case "month":
                // 本月：从本月1号到今天
                startDate = now.withDayOfMonth(1);
                break;
            case "year":
                // 本年：从1月1号到今天
                startDate = now.withDayOfYear(1);
                break;
            default:
                startDate = now.minusDays(6); // 默认最近7天
        }
        
        return new Date[]{
            Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()),
            Date.from(endDate.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant())
        };
    }

    /**
     * 生成日期列表
     */
    private List<String> generateDateList(String period, Date startDate, Date endDate) {
        List<String> dates = new ArrayList<>();
        SimpleDateFormat dateFormat = getDateFormat(period);
        
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endDate);
        
        if ("year".equals(period)) {
            // 按月生成
            for (int i = 1; i <= 12; i++) {
                dates.add(i + "月");
            }
        } else {
            // 按天生成
            while (!calendar.after(endCalendar)) {
                dates.add(dateFormat.format(calendar.getTime()));
                calendar.add(Calendar.DAY_OF_MONTH, 1);
            }
        }
        
        return dates;
    }

    /**
     * 获取日期格式化器
     */
    private SimpleDateFormat getDateFormat(String period) {
        switch (period) {
            case "week":
            case "month":
                return new SimpleDateFormat("M-d");
            case "year":
                return new SimpleDateFormat("M月");
            default:
                return new SimpleDateFormat("M-d");
        }
    }

    @Override
    public BillDetailVO getBillDetail(Integer year, Integer month, Long accountId, Long userId) {
        log.info("获取账单明细 - userId: {}, year: {}, month: {}, accountId: {}", userId, year, month, accountId);
        
        // 构建查询日期范围
        LocalDate startDate = LocalDate.of(year, month, 1);
        LocalDate endDate = startDate.plusMonths(1).minusDays(1);
        
        Date startDateTime = Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date endDateTime = Date.from(endDate.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());
        
        log.info("查询日期范围 - start: {}, end: {}", startDateTime, endDateTime);
        
        // 查询该月份的所有账单
        LambdaQueryWrapper<Tally> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Tally::getUserId, userId)
               .between(Tally::getCreateTime, startDateTime, endDateTime)
               .eq(Tally::getIsDeleted, 0);
        
        // 如果指定了账本ID，则只查询该账本的数据
        if (accountId != null) {
            wrapper.eq(Tally::getAccountId, accountId);
            log.info("添加账本过滤条件 - accountId: {}", accountId);
        }
        
        wrapper.orderByDesc(Tally::getCreateTime);
        
        List<Tally> tallyList = this.list(wrapper);
        log.info("查询到 {} 条账单记录", tallyList.size());
        
        // 统计总收入和总支出
        double totalIncome = 0.0;
        double totalExpense = 0.0;
        
        // 转换为BillItemVO列表
        List<BillItemVO> billItems = new ArrayList<>();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        
        for (Tally tally : tallyList) {
            // 获取分类名称
            Map<Long, String> categoryMap = (tally.getType() == 1) ? 
                EXPENSE_CATEGORY_MAP : INCOME_CATEGORY_MAP;
            String categoryName = categoryMap.getOrDefault(tally.getFlag(), "其他");
            
            // 创建BillItemVO
            BillItemVO billItem = new BillItemVO();
            billItem.setId(tally.getId());
            billItem.setDate(dateFormat.format(tally.getCreateTime()));
            billItem.setType(tally.getType());
            billItem.setCategory(tally.getFlag());
            billItem.setCategoryName(categoryName);
            billItem.setRemark(tally.getRemark());
            billItem.setAmount(tally.getPrice());
            
            billItems.add(billItem);
            
            // 统计收入支出
            if (tally.getType() == 1) {
                totalExpense += tally.getPrice();
            } else if (tally.getType() == 2) {
                totalIncome += tally.getPrice();
            }
        }
        
        // 构建返回结果
        BillDetailVO result = new BillDetailVO();
        result.setYear(year);
        result.setMonth(month);
        result.setTotalIncome(totalIncome);
        result.setTotalExpense(totalExpense);
        result.setBills(billItems);
        
        log.info("返回账单明细 - 收入: {}, 支出: {}, 账单数: {}", 
                 totalIncome, totalExpense, billItems.size());
        
        return result;
    }

    @Override
    public boolean saveTallyByDto(TallyDto tallyDto, Long userId) {
        log.info("保存记账记录 - userId: {}, type: {}, flag: {}, price: {}, createTime: {}", 
                 userId, tallyDto.getType(), tallyDto.getFlag(), tallyDto.getPrice(), tallyDto.getCreateTime());
        
        Tally tally = new Tally();
        BeanUtils.copyProperties(tallyDto, tally); // 复制共同字段

        // 设置用户ID（从Token获取，非前端传入）
        tally.setUserId(userId);
        
        // 设置创建人和最后更新人
        tally.setCreator(userId);
        tally.setLastUpdator(userId);
        
        // 处理创建时间：如果前端传了自定义时间就用自定义的，否则用当前时间
        if (tallyDto.getCreateTime() == null) {
            tally.setCreateTime(new Date());
            log.info("使用当前时间作为创建时间");
        } else {
            tally.setCreateTime(tallyDto.getCreateTime());
            log.info("使用自定义创建时间: {}", tallyDto.getCreateTime());
        }
        
        // 设置最后更新时间（与创建时间保持一致）
        tally.setLastUpdateTime(tally.getCreateTime());

        // 设置默认值（如果需要）
        tally.setIsDeleted(0); // 未删除

        // 调用父类方法保存
        boolean result = save(tally);
        
        if (result) {
            log.info("记账记录保存成功 - id: {}, createTime: {}", tally.getId(), tally.getCreateTime());
        } else {
            log.error("记账记录保存失败");
        }
        
        return result;
    }

    @Override
    public boolean updateTallyByDto(Long id, TallyDto tallyDto, Long userId) {
        log.info("更新记账记录 - id: {}, userId: {}, type: {}, flag: {}, price: {}", 
                 id, userId, tallyDto.getType(), tallyDto.getFlag(), tallyDto.getPrice());
        
        // 1. 先查询记录是否存在且属于该用户
        Tally existingTally = this.getById(id);
        if (existingTally == null) {
            log.error("记账记录不存在 - id: {}", id);
            throw new RuntimeException("记账记录不存在");
        }
        
        if (!existingTally.getUserId().equals(userId)) {
            log.error("无权限修改该记账记录 - id: {}, userId: {}, recordUserId: {}", 
                     id, userId, existingTally.getUserId());
            throw new RuntimeException("无权限修改该记账记录");
        }
        
        if (existingTally.getIsDeleted() == 1) {
            log.error("记账记录已被删除 - id: {}", id);
            throw new RuntimeException("记账记录已被删除");
        }
        
        // 2. 更新记录 - 只更新需要修改的字段，避免覆盖creator等字段
        Tally tally = new Tally();
        tally.setId(id);
        
        // 手动设置需要更新的字段（不使用BeanUtils.copyProperties，避免覆盖null值）
        if (tallyDto.getType() != null) {
            tally.setType(tallyDto.getType());
        }
        if (tallyDto.getFlag() != null) {
            tally.setFlag(tallyDto.getFlag());
        }
        if (tallyDto.getPrice() != null) {
            tally.setPrice(tallyDto.getPrice());
        }
        // remark允许为空字符串，所以不判断null
        tally.setRemark(tallyDto.getRemark());
        
        // 处理创建时间：如果前端传了自定义时间就用自定义的
        if (tallyDto.getCreateTime() != null) {
            tally.setCreateTime(tallyDto.getCreateTime());
            log.info("使用自定义创建时间: {}", tallyDto.getCreateTime());
        }
        
        // 设置最后更新人和更新时间
        tally.setLastUpdator(userId);
        tally.setLastUpdateTime(new Date());
        
        // 注意：不要设置creator、userId等不应该被更新的字段
        
        // 调用父类方法更新
        boolean result = updateById(tally);
        
        if (result) {
            log.info("记账记录更新成功 - id: {}", id);
        } else {
            log.error("记账记录更新失败 - id: {}", id);
        }
        
        return result;
    }

    @Override
    public boolean deleteTallyById(Long id, Long userId) {
        log.info("删除记账记录 - id: {}, userId: {}", id, userId);
        
        // 1. 先查询记录是否存在且属于该用户
        Tally existingTally = this.getById(id);
        if (existingTally == null) {
            log.error("记账记录不存在 - id: {}", id);
            throw new RuntimeException("记账记录不存在");
        }
        
        if (!existingTally.getUserId().equals(userId)) {
            log.error("无权限删除该记账记录 - id: {}, userId: {}, recordUserId: {}", 
                     id, userId, existingTally.getUserId());
            throw new RuntimeException("无权限删除该记账记录");
        }
        
        if (existingTally.getIsDeleted() == 1) {
            log.error("记账记录已被删除 - id: {}", id);
            throw new RuntimeException("记账记录已被删除");
        }
        
        // 2. 在删除前更新last_updator字段
        Tally updateTally = new Tally();
        updateTally.setId(id);
        updateTally.setLastUpdator(userId);
        updateTally.setLastUpdateTime(new Date());
        updateById(updateTally);
        
        // 3. 使用MyBatis-Plus的逻辑删除方法
        // removeById()会自动处理@TableLogic注解的字段，将is_deleted设置为1
        boolean result = removeById(id);
        
        if (result) {
            log.info("记账记录删除成功 - id: {}", id);
        } else {
            log.error("记账记录删除失败 - id: {}", id);
        }
        
        return result;
    }

    @Override
    public Object getAccountOverview(Long userId) {
        log.info("获取账户概览数据 - userId: {}", userId);

        // 查询用户的所有记账记录
        LambdaQueryWrapper<Tally> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Tally::getUserId, userId)
                .eq(Tally::getIsDeleted, 0)
                .orderByDesc(Tally::getCreateTime);

        List<Tally> allTally = this.list(wrapper);

        // 获取当前日期
        LocalDate now = LocalDate.now();
        int currentYear = now.getYear();
        int currentMonth = now.getMonthValue();

        // 初始化统计数据
        double totalIncome = 0.0;      // 总收入
        double totalExpense = 0.0;     // 总支出
        double monthlyIncome = 0.0;    // 本月收入
        double monthlyExpense = 0.0;   // 本月支出
        double yearlyIncome = 0.0;     // 本年收入
        double yearlyExpense = 0.0;    // 本年支出

        // 分类统计 (用于饼图)
        Map<String, Double> expenseByCategory = new HashMap<>();
        Map<String, Double> incomeByCategory = new HashMap<>();

        // 最近10条交易记录
        List<Map<String, Object>> recentRecords = new ArrayList<>();
        int recordCount = 0;

        // 遍历所有记录进行统计
        for (Tally tally : allTally) {
            LocalDate tallyDate = tally.getCreateTime().toInstant()
                    .atZone(ZoneId.systemDefault()).toLocalDate();

            boolean isCurrentMonth = tallyDate.getYear() == currentYear &&
                    tallyDate.getMonthValue() == currentMonth;
            boolean isCurrentYear = tallyDate.getYear() == currentYear;

            // 根据类型统计
            if (tally.getType() == 1L) {
                // 支出
                totalExpense += tally.getPrice();
                if (isCurrentMonth) monthlyExpense += tally.getPrice();
                if (isCurrentYear) yearlyExpense += tally.getPrice();

                // 分类统计
                String category = EXPENSE_CATEGORY_MAP.getOrDefault(tally.getFlag(), "其他");
                expenseByCategory.put(category,
                        expenseByCategory.getOrDefault(category, 0.0) + tally.getPrice());
            } else if (tally.getType() == 2L) {
                // 收入
                totalIncome += tally.getPrice();
                if (isCurrentMonth) monthlyIncome += tally.getPrice();
                if (isCurrentYear) yearlyIncome += tally.getPrice();

                // 分类统计
                String category = INCOME_CATEGORY_MAP.getOrDefault(tally.getFlag(), "其他");
                incomeByCategory.put(category,
                        incomeByCategory.getOrDefault(category, 0.0) + tally.getPrice());
            }

            // 收集最近10条记录
            if (recordCount < 10) {
                Map<String, Object> record = new HashMap<>();
                record.put("id", tally.getId());
                record.put("type", tally.getType());
                record.put("typeName", tally.getType() == 1L ? "支出" : "收入");
                record.put("category", tally.getType() == 1L ?
                        EXPENSE_CATEGORY_MAP.getOrDefault(tally.getFlag(), "其他") :
                        INCOME_CATEGORY_MAP.getOrDefault(tally.getFlag(), "其他"));
                record.put("amount", tally.getPrice());
                record.put("remark", tally.getRemark());
                record.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm").format(tally.getCreateTime()));
                recentRecords.add(record);
                recordCount++;
            }
        }

        // 转换分类统计为列表格式
        List<Map<String, Object>> expenseCategoryList = new ArrayList<>();
        for (Map.Entry<String, Double> entry : expenseByCategory.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("category", entry.getKey());
            item.put("amount", entry.getValue());
            item.put("percentage", totalExpense > 0 ? (entry.getValue() / totalExpense * 100) : 0);
            expenseCategoryList.add(item);
        }
        // 按金额降序排序
        expenseCategoryList.sort((a, b) ->
                Double.compare((Double)b.get("amount"), (Double)a.get("amount")));

        List<Map<String, Object>> incomeCategoryList = new ArrayList<>();
        for (Map.Entry<String, Double> entry : incomeByCategory.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("category", entry.getKey());
            item.put("amount", entry.getValue());
            item.put("percentage", totalIncome > 0 ? (entry.getValue() / totalIncome * 100) : 0);
            incomeCategoryList.add(item);
        }
        // 按金额降序排序
        incomeCategoryList.sort((a, b) ->
                Double.compare((Double)b.get("amount"), (Double)a.get("amount")));

        // 组装返回数据
        Map<String, Object> result = new HashMap<>();

        // 总体统计
        result.put("totalIncome", totalIncome);
        result.put("totalExpense", totalExpense);
        result.put("totalBalance", totalIncome - totalExpense);

        // 本月统计
        result.put("monthlyIncome", monthlyIncome);
        result.put("monthlyExpense", monthlyExpense);
        result.put("monthlyBalance", monthlyIncome - monthlyExpense);

        // 本年统计
        result.put("yearlyIncome", yearlyIncome);
        result.put("yearlyExpense", yearlyExpense);
        result.put("yearlyBalance", yearlyIncome - yearlyExpense);

        // 分类统计
        result.put("expenseByCategory", expenseCategoryList);
        result.put("incomeByCategory", incomeCategoryList);

        // 最近交易
        result.put("recentRecords", recentRecords);

        // 记录总数
        result.put("totalRecords", allTally.size());

        log.info("账户概览数据统计完成 - 总收入: {}, 总支出: {}, 记录数: {}",
                totalIncome, totalExpense, allTally.size());

        return result;
    }
}
